package com.example.forum.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 功能描述:
 *
 * @author Lenovo
 * @date 2025/2/18
 */
@Aspect
@Component
@Slf4j
public class PerformanceMonitorAspect {

    // 用于存储每个接口的性能统计数据
    private final ConcurrentHashMap<String, MethodStats> methodStats = new ConcurrentHashMap<>();

    @Pointcut("execution(* com.example.forum.controller.*.*(..))")
    public void controllerMethods() {
    }

    @Around("controllerMethods()")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = getMethodName(joinPoint);

        try {
            return joinPoint.proceed();
        } finally {
            long executionTime = System.currentTimeMillis() - startTime;
            updateStats(methodName, executionTime);
            logPerformance(methodName, executionTime);
        }
    }

    /**
     * 获取方法签名
     *
     * @param joinPoint
     * @return
     */
    private String getMethodName(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getDeclaringType().getSimpleName() + "." + signature.getName();
    }

    /**
     * 更细执行时间
     *
     * @param methodName
     * @param executionTime
     */
    private void updateStats(String methodName, long executionTime) {
        methodStats.computeIfAbsent(methodName, k -> new MethodStats())
                .addExecution(executionTime);
    }

    /**
     * 加入日志
     *
     * @param methodName
     * @param executionTime
     */
    private void logPerformance(String methodName, long executionTime) {
        MethodStats stats = methodStats.get(methodName);
        if (stats != null) {
            log.info("性能统计 - 方法: {} | 执行时间: {}ms | 平均时间: {}ms | 最大时间: {}ms | 最小时间: {}ms | 总调用次数: {}",
                    methodName,
                    executionTime,
                    stats.getAverageTime(),
                    stats.getMaxTime(),
                    stats.getMinTime(),
                    stats.getTotalCalls());
        }
    }

    // 内部类用于存储方法的性能统计数据
    private static class MethodStats {
        //使用原子类，保证准确性
        private final AtomicLong totalTime = new AtomicLong(0);
        private final AtomicLong totalCalls = new AtomicLong(0);
        private volatile long maxTime = 0;
        private volatile long minTime = Long.MAX_VALUE;

        public void addExecution(long executionTime) {
            totalTime.addAndGet(executionTime);
            totalCalls.incrementAndGet();
            updateMaxTime(executionTime);
            updateMinTime(executionTime);
        }

        /**
         * 更新最大执行时间
         *
         * @param executionTime
         */
        private void updateMaxTime(long executionTime) {
            long currentMax;
            do {
                currentMax = maxTime;
                if (executionTime <= currentMax) {
                    break;
                }
            } while (!compareAndSetMaxTime(currentMax, executionTime));
        }

        /**
         * 更新最小执行时间
         *
         * @param executionTime
         */
        private void updateMinTime(long executionTime) {
            long currentMin;
            do {
                currentMin = minTime;
                if (executionTime >= currentMin) {
                    break;
                }
            } while (!compareAndSetMinTime(currentMin, executionTime));
        }

        private boolean compareAndSetMaxTime(long expect, long update) {
            synchronized (this) {
                if (maxTime == expect) {
                    maxTime = update;
                    return true;
                }
                return false;
            }
        }

        private boolean compareAndSetMinTime(long expect, long update) {
            synchronized (this) {
                if (minTime == expect) {
                    minTime = update;
                    return true;
                }
                return false;
            }
        }

        public long getAverageTime() {
            long calls = totalCalls.get();
            return calls > 0 ? totalTime.get() / calls : 0;
        }

        public long getMaxTime() {
            return maxTime;
        }

        public long getMinTime() {
            return minTime == Long.MAX_VALUE ? 0 : minTime;
        }

        public long getTotalCalls() {
            return totalCalls.get();
        }
    }
}
